import { LogCategoryGroup } from './logCategoryGroup';
import { logger, LogLevel, LoggerType } from './logger';

export interface LogCategoryConfig {
  active?: boolean;
  prefix?: string;
}

export interface LogMessageInput {
  msg: string;
  data?: any;
  categories?: string | string[];
  prefix?: string;
}

export class LogCategory {
  private logger: LoggerType;
  private categories: Record<string, LogCategoryConfig> = {};
  private categoryGroup?: LogCategoryGroup;
  private groupStack: { label: string; startTime: number; prefix?: string }[] = [];

  constructor(initialCategories?: string[] | Record<string, LogCategoryConfig>) {
    this.logger = logger;

    if (Array.isArray(initialCategories)) {
      initialCategories.forEach((cat) => {
        this.categories[cat] = { active: false };
      });
    } else if (initialCategories) {
      this.categories = { ...initialCategories };
      Object.keys(this.categories).forEach((key) => {
        if (this.categories[key].active === undefined) {
          this.categories[key].active = false;
        }
      });
    }
  }

  bindCategoryGroup(group: LogCategoryGroup) {
    this.categoryGroup = group;
    return this;
  }

  getAllRegisteredCategories(): Record<string, LogCategoryConfig> {
    return this.categories;
  }

  enableAll() {
    Object.values(this.categories).forEach((cat) => (cat.active = true));
    this.categoryGroup?.enableAll();
  }

  disableAll() {
    Object.values(this.categories).forEach((cat) => (cat.active = false));
    this.categoryGroup?.disableAll();
  }

  enableCategories(names: string[]) {
    names.forEach((name) => {
      if (this.categories[name]) this.categories[name].active = true;
    });
  }

  disableCategories(names: string[]) {
    names.forEach((name) => {
      if (this.categories[name]) this.categories[name].active = false;
    });
  }

  // -------------------- 日志输出控制 --------------------
  private resolveCategories(input?: string | string[]): string[] {
    let keys: string[] = [];
    if (!input) keys = Object.keys(this.categories);
    else if (typeof input === 'string') keys = [input];
    else keys = input;

    const resolved: string[] = [];

    keys.forEach((name) => {
      const cat = this.categories[name];
      if (!cat) {
        console.warn(`[LogCategory] 分类 "${name}" 未注册`);
        return;
      }

      if (cat.active) {
        resolved.push(name);
        return;
      }

      // 分类未启用 -> 检查它是否属于任何启用的分组
      if (this.categoryGroup) {
        const groups = this.categoryGroup.getGroupsForCategory(name);
        for (const gName of groups) {
          const gCfg = this.categoryGroup.getGroup(gName);
          if (gCfg?.active) {
            resolved.push(name);
            return;
          }
        }
      }
    });

    return resolved;
  }

  private formatMessage(message: string, categories?: string[], prefix?: string) {
    const catStr = categories && categories.length > 0 ? `[${categories.join(', ')}] ` : '';
    const prefixStr = prefix ? `[${prefix}] ` : '';
    return `${prefixStr}${catStr}${message}`;
  }

  private log(level: LogLevel, ...args: any[]) {
    let msg: string;
    let categories: string | string[] | undefined;
    let prefix: string | undefined;
    let data: any;

    // 对象调用：只有一个参数且为对象
    if (args.length === 1 && typeof args[0] === 'object' && args[0].msg) {
      const input: LogMessageInput = args[0];
      msg = input.msg;
      categories = input.categories;
      prefix = input.prefix;
      data = input.data;
    } else {
      // 原生调用： msg, categories?, ...data
      msg = args[0];
      categories = args[1];
      data = args.slice(2);
      if (data.length === 1) data = data[0]; // 单个数据简化
    }

    const cats = this.resolveCategories(categories);
    if (!cats.length) return;

    const formatted = this.formatMessage(msg, cats, prefix);

    switch (level) {
      case LogLevel.DEBUG:
        {
          if (data !== undefined) {
            this.logger.debug(formatted, data);
          } else {
            this.logger.debug(formatted);
          }
        }
        break;
      case LogLevel.INFO:
        {
          if (data !== undefined) {
            this.logger.info(formatted, data);
          } else {
            this.logger.info(formatted);
          }
        }
        break;
      case LogLevel.WARN:
        {
          if (data !== undefined) {
            this.logger.warn(formatted, data);
          } else {
            this.logger.warn(formatted);
          }
        }
        break;
      case LogLevel.ERROR:
        {
          if (data !== undefined) {
            this.logger.error(formatted, data);
          } else {
            this.logger.error(formatted);
          }
        }
        break;
    }
  }

  debug(msg: string | LogMessageInput, ...rest: any[]) {
    this.log(LogLevel.DEBUG, msg, ...rest);
  }

  info(msg: string | LogMessageInput, ...rest: any[]) {
    this.log(LogLevel.INFO, msg, ...rest);
  }

  warn(msg: string | LogMessageInput, ...rest: any[]) {
    this.log(LogLevel.WARN, msg, ...rest);
  }

  error(msg: string | LogMessageInput, ...rest: any[]) {
    this.log(LogLevel.ERROR, msg, ...rest);
  }

  // -------------------- 分组 --------------------
  group(label: string, categories?: string | string[], fn?: () => void, autoEnd: boolean = true) {
    const cats = this.resolveCategories(categories);
    if (!cats.length) return;

    let prefix: string | undefined;
    if (typeof categories === 'string') {
      const groupCfg = this.categoryGroup?.getGroup(categories);
      if (groupCfg?.prefix) prefix = groupCfg.prefix;
    }

    const msg = this.formatMessage(label, cats, prefix);
    this.logger.group(msg);

    this.groupStack.push({ label: msg, startTime: Date.now(), prefix });

    if (fn) {
      try {
        fn();
      } finally {
        if (autoEnd) this.groupEnd();
      }
    }
  }

  groupEnd() {
    const group = this.groupStack.pop();
    this.logger.groupEnd();
    if (group) {
      const duration = Date.now() - group.startTime;
      this.logger.info(`分组 "${group.label}" 耗时: ${duration}ms`);
    }
  }
}
